/**
 * udes.ds.agent.hw
 * AbstractEquation.java
 * 7 oct. 09
 */
package udes.ds.agent;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * TODO add type comments
 * 
 * @author Luc Bergevin
 * @version 1.0
 * @see
 */
public abstract class AbstractEquation implements Equation {
	private static final long serialVersionUID = 1822103035739880637L;

	private static HashMap<Equation, List<Equation>> allResult = new HashMap<Equation, List<Equation>>();

	public static boolean verifyDerive(Equation base, Equation derive) {
		double avg = AbstractEquation.verifyDeriveDistance(base, derive);
		double margeErreurInf = 0.99999;
		double margeErreurSup = 1.00001;
		if (margeErreurInf < avg && avg < margeErreurSup) {
			return true;
		}
		return false;
	}

	public static Double verifyDeriveDistance(Equation base, Equation derive) {
		if (derive == null || base == null) {
			return Double.MAX_VALUE;
		}

		List<Equation> wrong = allResult.get(base);
		if (wrong == null) {
			wrong = new ArrayList<Equation>();
			allResult.put(base, wrong);
		}

		if (wrong.contains(derive)) {
			return Double.MAX_VALUE;
		}

		int baseTest = -10000;
		int nbTest = 2000;
		double[] valueToTest = new double[nbTest];
		for (int i = 0; i < nbTest; i++) {
			valueToTest[i] = i + baseTest;
		}

		double marge = 0.0001;

		double distance = 0;
		for (double i : valueToTest) {
			Double deriveExpect = (base.getFunctionValue(i + marge) - base
					.getFunctionValue(i)) / marge;
			Double deriveValue = derive.getFunctionValue(i);

			if (deriveExpect.isInfinite() || deriveExpect.isNaN()) {
				// do not need to compare
				continue;
			}

			if (deriveValue.equals(Double.NaN)) {
				distance = Double.MAX_VALUE;
				break;
			}

			if (!deriveExpect.equals(deriveValue)) {
				if (deriveValue == 0) {
					deriveValue = 1.0;
					deriveExpect++;
				}
				distance += Math.abs(deriveExpect / deriveValue);
			}
		}
		double avg = distance / valueToTest.length;

		return avg;
	}

	/**
	 * Returns a user-readable form of the equation
	 * 
	 * @return String
	 */
	@Override
	public abstract String getUserReadableString();

	/**
	 * @see udes.ds.rmi.hw.Equation#printUserReadable()
	 */
	@Override
	public void printUserReadable() {
		System.out.println("y = " + this.getUserReadableString());
	}

	@Override
	public boolean isTerminal() {
		return false;
	}
}
